package com.franklin.java.service.database.ui;

import com.franklin.java.api.classloader.ExternalJarsClassLoader;
import com.franklin.java.api.ui.MessageActionListener;
import com.franklin.java.api.ui.jpanel.AbstractComponent;
import com.franklin.java.api.ui.jpanel.button.MyButton;
import com.franklin.java.entity.sql.JdbcInfo;
import com.franklin.java.service.database.FranklinJdbcConsole;
import com.franklin.java.service.database.JdbcService;
import com.franklin.java.service.database.JdbcState;
import com.franklin.java.utils.SwingUtils;
import com.franklin.java.utils.VirtualFileUtils;
import com.franklin.plugins.common.utils.DataCheckUtils;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import org.apache.commons.lang3.StringUtils;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.net.MalformedURLException;
import java.sql.Connection;
import java.util.Objects;

/**
 * @author Franklin
 * @since 2021/9/24 15:01
 */
public class AddDataSourceForm extends AbstractComponent {
    /**
     * 主面板
     */
    private JPanel mainPanel;
    private MyButton okButton;
    private JTextField usernameTextField;
    private JTextField passwordTextField;
    private JTextField urlTextField;
    private JTextField jdbcDriverPathTextField;
    private JPanel testConnectionPanel;
    private MyButton openJarButton;
    private JTextField driverClassTextField;
    private MyButton testConnectionButton;
    private JTextField dataSourceNameTextField;

    private MyButton cancelButton;

    private Project project;
    private FranklinJdbcConsole franklinJdbcConsole;

    public AddDataSourceForm(Project project, FranklinJdbcConsole franklinJdbcConsole) {
        this.project = project;
        this.franklinJdbcConsole = franklinJdbcConsole;
        init();
    }

    @Override
    public JPanel getMainPanel() {
        return mainPanel;
    }

    public MyButton getCancelButton() {
        return cancelButton;
    }

    /**
     * 初始化<br>
     *
     * @param :
     * @return void
     * @author Franklin
     * @since 2021/9/24 15:16
     */
    private void init() {
        this.cancelButton = new MyButton();
        initTestConnectionButton();
        initOpenJarButton();
        initTestPanel();
        initOkButton();
    }

    private void initOkButton() {
        this.okButton.addActionListener(
            new MessageActionListener<Void>(
                "",
                false, this.testConnectionPanel
            ) {
                @Override
                protected Void handleActionEvent(ActionEvent e) throws Exception {
                    String dataSourceName = dataSourceNameTextField.getText();
                    DataCheckUtils.assertIfEmpty(dataSourceName, "DataSourceName is empty");
                    if (dataSourceName.equals("Add DataSource")) {
                        throw new IllegalArgumentException("DataSourceName cannot be : \"Add DataSource\"");
                    }
                    JdbcInfo jdbcInfo = getJdbcInfo();
                    JdbcState.addJdbcInfo(dataSourceName, jdbcInfo);
                    franklinJdbcConsole.setJdbcInfo(jdbcInfo);
                    franklinJdbcConsole.executeQuery();
                    cancelButton.doClick();
                    return null;
                }
            });
    }

    private void initTestPanel() {
        this.testConnectionPanel.setLayout(SwingUtils.createLayoutManager(1, 1));
    }

    /**
     * 初始化打开Jar包的按钮<br>
     *
     * @param :
     * @return void
     * @author Franklin
     * @since 2021/9/24 15:18
     */
    private void initOpenJarButton() {
        this.openJarButton.addActionListener(e -> {
            VirtualFile jarFile = FileChooser.chooseFile(
                new FileChooserDescriptor(
                    false,
                    false,
                    true,
                    false,
                    false,
                    false),
                project,
                null
            );
            if (Objects.nonNull(jarFile)) {
                this.jdbcDriverPathTextField.setText(jarFile.getPath().replace("!/", ""));
                if (!isLoadDriverClass()) {
                    loadDriverClass();
                }
            }
        });
    }

    /**
     * 获取连接信息对象<br>
     *
     * @param :
     * @return com.franklin.java.entity.sql.JdbcInfo
     * @author Franklin
     * @since 2021/9/24 16:23
     */
    private JdbcInfo getJdbcInfo() throws Exception {
        String username = this.usernameTextField.getText();
        String password = this.passwordTextField.getText();
        String url = this.urlTextField.getText();
        String driverClassName = this.driverClassTextField.getText();
        String driverPath = this.jdbcDriverPathTextField.getText();
        if (StringUtils.isAnyEmpty(username, password, url, driverClassName, driverPath)) {
            throw new IllegalArgumentException("jdbc param error");
        }
        JdbcInfo jdbcInfo = new JdbcInfo();
        jdbcInfo.setUsername(username);
        jdbcInfo.setPassword(password);
        jdbcInfo.setUrl(url);
        jdbcInfo.setDriverClassName(driverClassName);
        jdbcInfo.setJdbcDriverPath(driverPath);
        if (DataCheckUtils.isNotEmpty(driverPath)) {
            VirtualFile driverFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(driverPath);
            if (Objects.nonNull(driverFile)) {
                File file = VirtualFileUtils.convertToIOFile(driverFile);
                try {
                    jdbcInfo.setDriverUrl(file.toURI().toURL());
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            }
        }
        return jdbcInfo;
    }

    /**
     * 初始化测试按钮<br>
     *
     * @param :
     * @return void
     * @author Franklin
     * @since 2021/9/24 15:17
     */
    private void initTestConnectionButton() {
        this.testConnectionButton.addActionListener(
            new MessageActionListener<Void>(
                "Connect Success",
                false, this.testConnectionPanel
            ) {
                @Override
                protected Void handleActionEvent(ActionEvent e) throws Exception {
                    if (!isLoadDriverClass()) {
                        loadDriverClass();
                    }
                    JdbcInfo jdbcInfo = getJdbcInfo();
                    JdbcService jdbcService = JdbcService.getInstance();
                    jdbcService.addDataSource(jdbcInfo);
                    Connection connection = null;
                    try {
                        connection = jdbcService.getConnection(jdbcInfo);
                    }catch (Exception exception){
                        throw new RuntimeException("connection failed");
                    } finally {
                        if (Objects.nonNull(connection)) {
                            connection.close();
                        }
                    }
                    return null;
                }
            });
    }

    /**
     * 判断是否加载了驱动类<br>
     *
     * @param :
     * @return boolean
     * @author Franklin
     * @since 2021/9/24 16:31
     */
    private boolean isLoadDriverClass() {
        Thread currentThread = Thread.currentThread();
        ClassLoader originClassLoader = currentThread.getContextClassLoader();
        try {
            JdbcInfo jdbcInfo = getJdbcInfo();
            currentThread.setContextClassLoader(ExternalJarsClassLoader.getClassLoader(jdbcInfo.getDriverUrl()));
            Class.forName(jdbcInfo.getDriverClassName());
            return true;
        } catch (Exception e) {
            return false;
        } finally {
            currentThread.setContextClassLoader(originClassLoader);
        }
    }

    private boolean loadDriverClass() {
        String driverPath = this.jdbcDriverPathTextField.getText();
        VirtualFile driverFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(driverPath);
        if (Objects.nonNull(driverFile)) {
            VirtualFileUtils.loadJar(driverFile);
        }
        return isLoadDriverClass();
    }

}
